Introduction

Bike Share Systems Role in Urban Mobilities

Bike share system are one of the critical components of urban mobility. They provide a flexible and convenient mode of transportation for short trips, reducing congestion and promoting sustainable travel. The systems are designed to be user-friendly, with easy access to bikes and docking stations, making them an attractive option for commuters and tourists alike. At the same time, bike sharing systems fill a gap in the public transportation system, providing a last-mile solution for users who need to travel short distances. By integrating bike sharing with other modes of transportation, such as buses and trains, cities can create a more efficient and sustainable transportation network.

Rebalancing Problems in Bike Sharing Systems

One of the key operational challenges in bike-share system is to maintain a balanced distribution across stations and times to meet user demands. Some stations frequently experience bike shortages where the users cannot get a bike from the docks when they need them. However, some stations frequently experience surpluses where the users cannot return the bikes to the docks when they need them. This rebalance issues become extremely serious during the peak hours due to assymetric commuting patterns. Without timely rebalancing, users may find it difficult to rent or return bikes on timly manners, leading to user dissatisfaction and reduced ridership. Efficient reblancing ahead of user demands ensures the system’s availability and reliability, which also involves real-time data analysis and prediction.

Proposed Rebalancing Strategies

To address this issue, we propose a data-driven approach to predict the bike demand across each stations and time intervals. By analyzing historical data, we can identify patterns and trends in bike usage, allowing us to make informed decisions about where and when to redistribute bikes. This approach not only improves the efficiency of the bike-share system but also enhances the overall user experience by ensuring that bikes are available when and where they are needed. Predictions will be made several hours in advance using the models that incorporate features such as time of day, day of week, historical usage patterns, weather conditions, and current station capacity. By leveraging these data sources, we can optimize the distribution of bikes across the system, reducing the likelihood of shortages and surpluses.

The model will be focused rebalance efforts on the stations in Philadelphia, PA. The model will be trained using the historical bike share data for the month of May, 2022. The model will be used to predict the bike demand for the next 24 hours, and the results will be used to inform rebalancing efforts.

Data Preprocessing and Feature Engineering

Data Preparation

Bike data

The bike share data is obtained from the Indego. The data includes information on bike trips, including start and end times, start and end stations, and bike IDs. The data is cleaned and preprocessed to remove any duplicates or missing values. The data is then aggregated to hourly intervals to create a time series of bike demand for each station. May 2022 data was selected for this analysis as the temporal frame.

bike<-read.csv("data/indego-trips-2022-q2.csv")


may <- bike %>%
  mutate(start_time = mdy_hm(start_time)) %>%
  filter(month(start_time) == 5) %>%
  mutate(interval60 = floor_date(ymd_hms(start_time), unit = "hour"),
         interval15 = floor_date(ymd_hms(start_time), unit = "15 mins"),
         week = week(interval60),
         dotw = wday(interval60, label=TRUE))

may<-may%>%
  filter(!is.na(ymd_hms(start_time)))

Population data

At the same time, census tract geometries of the entire Philadelphia county was acquired from the US Census Bureau to provide the geographic zone boundaries for further analysis if needed.

philCensus <-
  get_acs(geography = "tract",
          variables = c("B01003_001", "B19013_001",
                        "B02001_002", "B08013_001",
                        "B08012_001", "B08301_001",
                        "B08301_010", "B01002_001"),
          year = 2022,
          state = "PA",
          geometry = TRUE,
          county=c("Philadelphia"),
          output = "wide") %>%
  rename(Total_Pop =  B01003_001E,
         Med_Inc = B19013_001E,
         Med_Age = B01002_001E,
         White_Pop = B02001_002E,
         Travel_Time = B08013_001E,
         Num_Commuters = B08012_001E,
         Means_of_Transport = B08301_001E,
         Total_Public_Trans = B08301_010E) %>%
  select(Total_Pop, Med_Inc, White_Pop, Travel_Time,
         Means_of_Transport, Total_Public_Trans,
         Med_Age,
         GEOID, geometry) %>%
  mutate(Percent_White = White_Pop / Total_Pop,
         Mean_Commute_Time = Travel_Time / Total_Public_Trans,
         Percent_Taking_Public_Trans = Total_Public_Trans / Means_of_Transport)

philTracts <-
  philCensus %>%
  as.data.frame() %>%
  distinct(GEOID, .keep_all = TRUE) %>%
  select(GEOID, geometry) %>%
  st_sf

Since the raw bikeshare data is a non-spatial layer, only containing latitute and longitude of the start and end stations, we need to join the bikeshare data with the census tract data to obtain the census tract information for each trip. The st_join function from the sf package is used to perform a spatial join between the bikeshare data and the census tract data. We are only interested in the start location of each ride because we want to predict the capacity of each station when the user need a bike to start the ride. This requires us to filter out rows with missing latitude and longitude.

dat_census <- st_join(may %>%
          filter(is.na(start_lon) == FALSE &
                   is.na(start_lat) == FALSE &
                   is.na(end_lon) == FALSE &
                   is.na(end_lat) == FALSE) %>%
          st_as_sf(., coords = c("start_lon", "start_lat"), crs = 4326),
        philTracts %>%
          st_transform(crs=4326),
        join=st_intersects,
              left = TRUE) %>%
  rename(Origin.Tract = GEOID) %>%
  mutate(start_lon = unlist(map(geometry, 1)),
         start_lat = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  select(-geometry)%>%
  st_as_sf(., coords = c("end_lon", "end_lat"), crs = 4326) %>%
  st_join(., philTracts %>%
            st_transform(crs=4326),
          join=st_intersects,
          left = TRUE) %>%
  rename(Destination.Tract = GEOID)  %>%
  mutate(to_longitude = unlist(map(geometry, 1)),
         to_latitude = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  select(-geometry)

The following map shows all the starting point of the rides in Philadelphia during May 2022.

ggplot()+
  geom_sf(data = philTracts %>%
          st_transform(crs=4326), fill="white")+
  geom_point(data = may, aes(x=start_lon, y = start_lat), 
            color = "#6E0E0A", alpha = 1, size = 0.3) + 
  ylim(min(may$start_lat), max(may$start_lat))+
  xlim(min(may$start_lon), max(may$start_lon)) +
  labs(title = "Location of Rides Starting Points in Philadelphia")+
  mapTheme

### Weather data The weather data is obtained from the National Weather Service (Philadelphia International Airport). The data includes information on temperature, precipitation, and wind speed. The data is cleaned and preprocessed to remove any duplicates or missing values. The data is then aggregated to hourly intervals to create a time series of weather conditions for each station.

weather.Panel <-
  riem_measures(station = "PHL", date_start = "2022-05-01", date_end = "2022-05-31") %>%
  dplyr::select(valid, tmpf, p01i, sknt)%>%
  replace(is.na(.), 0) %>%
    mutate(interval60 = ymd_h(substr(valid,1,13))) %>%
    mutate(week = week(interval60),
           dotw = wday(interval60, label=TRUE)) %>%
    group_by(interval60) %>%
    summarize(Temperature = max(tmpf),
              Precipitation = sum(p01i),
              Wind_Speed = max(sknt)) %>%
    mutate(Temperature = ifelse(Temperature == 0, 42, Temperature))

Feature Engineering & Exploratory Analysis

Temporal Analysis

Starting with temporal analysis, we first visualized the bikeshare trips per hour in Philadelphia in May 2023. It is clear that there’s some form of circularity in the data at both peak hours and non-peak hours during single day. We may also see that after five number of high peaks are usually by two lower peaks (weekday and weekend). These correspond the trends mentioned in the introduction section that bikeshare are much more needed during the weekdays rush hours compared to the weekends and late night hours.

ggplot(dat_census %>%
         group_by(interval60) %>%
         tally())+
  geom_line(aes(x = interval60, y = n))+
  labs(title="Bike share trips per hr. Philadelphia, May, 2022",
       x="Date",
       y="Number of trips")+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Then, we visualize the distribution of trips by stations at each hour in May. The distribution is right skewed, where most stations have zero or very few number of trips each hour. However, there are few stations that have a very high number of trips each hour on particular day in May 2022.

ggplot(dat_census %>%
         group_by(interval60, start_station) %>%
         tally())+
  geom_histogram(aes(n), binwidth = 5, fill="#6E0E0A")+
  labs(title="Bike share trips per hour by station. Philadelphia, May, 2022",
       x="Trip Counts",
       y="Number of Stations")+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Temporary Feature Engineer

We perform feature engineering using lubrudate package to extract temporal information for each usage. the usage was calculating into 60 min and 15 min interval based on staring time for each ride. And then, the ride was further categorize into “Overnight”, “AM Rush”, “Mid-Day” and “PM Rush” based on the hour of the day and into weekday and weekend based on the day of the week.

We visualize the distribution of the mean number trips at each station during different time intervals of the day. During the PM rush hour, the average number of trips is the highest, followed by the mid-day and AM rush hour. The overnight period has the lowest average number of trips, with only few station with an average of 3 trips or more.

palette4 <- c("#124E78","#F0F0C9","#F2BB05","#6E0E0A")
dat_census %>%
        mutate(time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
         group_by(interval60, start_station, time_of_day) %>%
         tally()%>%
  group_by(start_station, time_of_day)%>%
  summarize(mean_trips = mean(n))%>%
  ggplot()+
  geom_histogram(aes(mean_trips, fill=time_of_day), binwidth = 1)+
  scale_fill_manual(values = palette4, name="Time of Day") +
  labs(title="Mean Number of Hourly Trips Per Station. Philadelphia, May, 2022",
       x="Number of trips",
       y="Frequency")+
  facet_wrap(~time_of_day)+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

After that, we visualize the distribution of the mean number of trips at each station during different days of the week. From the chart, we can see that the Monday, Tuesday, Wednesday, and Thursday shows similar temporal patterns, with similar and highest number of trips by the time of the day. The Friday and Sturday shows similar patterns, with smaller number of people utilize the system. And, Sunday shows a uniquepeak pattern than any other weekdays or weekends.But in general, lowest trip count occur in the early morning while highest trip count occur in the late afternoon.

ggplot(dat_census %>% mutate(hour = hour(start_time)))+
     geom_freqpoly(aes(hour, color = dotw), binwidth = 3,lwd=0.8)+
  labs(title="Bike share trips in Philadelphia, by day of the week, May, 2022",
       x="Hour",
       y="Trip Counts")+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

We also compared the different usage between weekdays and weekends. The chart shows that the weekday usage is much higher than the weekend usage. The weekday usage shows a smaller peak during the AM rush hour and an even bigger peak during the PM rush hour. The weekend usage shows a smaller peak during the late morning and early afternoon, with a gradual decline in usage throughout the day.

ggplot(dat_census %>%
         mutate(hour = hour(start_time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday")))+
     geom_freqpoly(aes(hour, color = weekend), binwidth = 1)+
  labs(title="Bike share trips in Philadelphia - weekend vs weekday, May, 2022",
       x="Hour",
       y="Trip Counts")+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Spatial and Temporal Analysis

We visualize the distribution of the trips count at each station during different time intervals on weekday and weekend. As the map shows below, it indicate that the weekday usage is much higher than the weekend usage. On both weekday and weekend, most of the trip starts station clustered around the central city Philadelphia area, with a few trips starts in the west Philadelphia, South Philadelphia. These distribution suggests that trip demands is especially higher in the central city area and major transportation facilities. There is probably need to rebalance the bikes in these areas to meet the demand.

ggplot()+
  geom_sf(data = philTracts %>%
          st_transform(crs=4326))+
  geom_point(data = dat_census %>%
            mutate(hour = hour(start_time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
                time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
              group_by(start_station, start_lat, start_lon, weekend, time_of_day) %>%
              tally(),
            aes(x=start_lon, y = start_lat, color = n),
            fill = "transparent", alpha = 0.4, size = 3)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  facet_grid(weekend ~ time_of_day)+
  labs(title="Bike share trips per hr by station. Philadelphia, May, 2022")+
  mapTheme

Weather Analysis

May in Philadelphia was characterized by steadily warming spring days—morning lows in the 50s °F climbing to highs in the mid-80s by month’s end—with regular 15–20 °F. Percipitation were d mostly light tbut with a few intense downpours at the end of the month. Winds generally hovered around 5–15 mph. These brief wet and blustery interludes punctuated otherwise mild, comfortable weather throughout May.

grid.arrange(
  ggplot(weather.Panel, aes(interval60,Precipitation)) + geom_line() +
  labs(title="Percipitation", x="Hour", y="Perecipitation") + theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  ggplot(weather.Panel, aes(interval60,Wind_Speed)) + geom_line() +
    labs(title="Wind Speed", x="Hour", y="Wind Speed") +theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  ggplot(weather.Panel, aes(interval60,Temperature)) + geom_line() +
    labs(title="Temperature", x="Hour", y="Temperature") +theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  top="Weather Data - Philadelphia (PHL) - May, 2022")

Space-Time Panel

We created a study panel where each instance in the panel is a unique combination of space and time. In other words, each row will represent the ride at a particular station during a particular hour. After that, we add some more information to this panel. This includes counting the number of rides at this station at this particular hour, adding weather information, and bringing in census data.

study.panel <-
  expand.grid(interval60=unique(dat_census$interval60),
              start_station = unique(dat_census$start_station)) %>%
  left_join(., dat_census %>%
              select(start_station, Origin.Tract, start_lon, start_lat )%>%
              distinct() %>%
              group_by(start_station) %>%
              slice(1))

ride.panel <-
  dat_census %>%
  mutate(Trip_Counter = 1) %>%
  right_join(study.panel) %>%
  group_by(interval60, start_station,  Origin.Tract, start_lon, start_lat) %>%
  summarize(Trip_Count = sum(Trip_Counter, na.rm=T)) %>%
  left_join(weather.Panel) %>%
  ungroup() %>%
  filter(is.na(start_station) == FALSE) %>%
  mutate(week = week(interval60),
         dotw = wday(interval60, label = TRUE)) %>%
  filter(is.na(Origin.Tract) == FALSE)

ride.panel <-
  left_join(ride.panel, philCensus %>%
              as.data.frame() %>%
              select(-geometry), by = c("Origin.Tract" = "GEOID"))

ride.panel <-
  ride.panel %>%
  arrange(start_station, interval60) %>%
  mutate(lagHour = dplyr::lag(Trip_Count,1),
         lag2Hours = dplyr::lag(Trip_Count,2),
         lag3Hours = dplyr::lag(Trip_Count,3),
         lag4Hours = dplyr::lag(Trip_Count,4),
         lag12Hours = dplyr::lag(Trip_Count,12),
         lag1day = dplyr::lag(Trip_Count,24),
         holiday = ifelse(yday(interval60) == 148,1,0)) %>%
   mutate(day = yday(interval60)) %>%
   mutate(holidayLag = case_when(dplyr::lag(holiday, 1) == 1 ~ "PlusOneDay",
                                 dplyr::lag(holiday, 2) == 1 ~ "PlustTwoDays",
                                 dplyr::lag(holiday, 3) == 1 ~ "PlustThreeDays",
                                 dplyr::lead(holiday, 1) == 1 ~ "MinusOneDay",
                                 dplyr::lead(holiday, 2) == 1 ~ "MinusTwoDays",
                                 dplyr::lead(holiday, 3) == 1 ~ "MinusThreeDays"),
         holidayLag = ifelse(is.na(holidayLag) == TRUE, 0, holidayLag))

Weather Correlation

With the study panel, we could perform some exploratory analysis to prepare for further regression model. First, we want to see the relationship between the trip counts and the weather conditions. The figure below shows a generally significant, positive, and strong linear relationship between temperature and trip counts. This means in general, when the temperature increase, bikeshare demand tend to increase as well.

ride.panel %>%
  group_by(interval60) %>% 
  summarize(Trip_Count = mean(Trip_Count),
            Temperature = first(Temperature)) %>%
  mutate(week = week(interval60)) %>%
  ggplot(aes(Temperature, Trip_Count)) + 
    geom_point(color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
    facet_wrap(~week, ncol=8) + 
    labs(title="Trip Count As a Fuction of Temperature by Week",
         x="Temperature", y="Mean Trip Count") + 
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x = element_blank(),
    axis.ticks.x = element_blank(), 
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

We would also like to see the relationship between the trip counts and the wind speed. The figure below shows in the first two weeks, there is a generally negative linear relationship between wind speed and trip counts. However, in the last two weeks, the relationship is positive. These non-constant relationship may indicate that the wind speed is not the sole predictors influence the ridership.

ride.panel %>%
  group_by(interval60) %>% 
  summarize(Trip_Count = mean(Trip_Count),
            Wind = first(Wind_Speed)) %>%
  mutate(week = week(interval60)) %>%
  ggplot(aes(Wind, Trip_Count)) + 
    geom_point(color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
    facet_wrap(~week, ncol=8) + 
    labs(title="Trip Count As a Fuction of Wind by Week",
         x="Wind Speed", y="Mean Trip Count") + 
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x = element_blank(),
    axis.ticks.x = element_blank(), 
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)) + 
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x = element_blank(),
    axis.ticks.x = element_blank(), 
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Serial Correlation

If trip counts exhibit temporal autocorrelation, incorporating lagged features would improve prediction accuracy. Trip volumes at a given hour are often strongly correlated with those in the preceding and following hours, so knowing the count at one time point makes it easier to estimate volumes in adjacent intervals. To capture this effect, we engineered six distinct types of hourly lag features.

The table and charts below show the Pearson correlations between current trip counts and six lagged variables. Correlation strength falls off as lag increases: immediate (1 h) and daily (24 h) lags show the strongest positive links, mid-range lags weaken steadily, and the 12-hour lag even flips to a negative relationship. These results show that the best predictors are the hour just before and the same hour on the previous day, while hours farther away are less helpful—and the hour 12 hours earlier can even show an opposite trend.

library(dplyr)
library(tidyr)
library(kableExtra)

# compute mean-by-hour, gather lags, then corr by lag variable
corr_df <- as.data.frame(ride.panel) %>%
  group_by(interval60) %>%
  summarise_at(vars(starts_with("lag"), "Trip_Count"), mean, na.rm = TRUE) %>%
  gather(Variable, Value, -interval60, -Trip_Count) %>%
  mutate(Variable = factor(
    Variable,
    levels = c("lagHour","lag2Hours","lag3Hours","lag4Hours","lag12Hours","lag1day")
  )) %>%
  group_by(Variable) %>%
  summarise(Correlation = round(cor(Value, Trip_Count), 2)) %>%
  ungroup()

# render with kableExtra
corr_df %>%
  kable(
    format = "html",
    col.names = c("Lag Variable", "Correlation with Trip Count"),
    caption = "Correlation Between Lagged Trip Counts and Current Trip Count"
  ) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  )
Correlation Between Lagged Trip Counts and Current Trip Count
Lag Variable Correlation with Trip Count
lagHour 0.90
lag2Hours 0.73
lag3Hours 0.53
lag4Hours 0.35
lag12Hours -0.52
lag1day 0.80
as.data.frame(ride.panel) %>%
    group_by(interval60) %>% 
    summarise_at(vars(starts_with("lag"), "Trip_Count"), mean, na.rm = TRUE) %>%
    gather(Variable, Value, -interval60, -Trip_Count) %>%
  ggplot(aes(x = Value , y = Trip_Count)) +
    geom_point(size = 1, color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
  facet_wrap(~Variable, ncol = 6)+
  labs(x = "Variable", y = "Correlation", title = "Correlation Between Serial Lag Trips and Trip Count") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x = element_blank(),
    axis.ticks.x = element_blank(), 
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

ride.Train <- filter(ride.panel, week >= 20)
ride.Test <- filter(ride.panel, week < 20)
reg1 <-
  lm(Trip_Count ~  factor(hour(interval60)) + factor(dotw) + Temperature,  data=ride.Train)

reg2 <-
  lm(Trip_Count ~  start_station +  factor(dotw)+ Temperature,  data=ride.Train)

reg3 <-
  lm(Trip_Count ~  start_station + factor(hour(interval60)) + factor(dotw) + Temperature + Precipitation,
     data=ride.Train)

reg4 <-
  lm(Trip_Count ~  start_station +  factor(hour(interval60)) +  factor(dotw) + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours + lag12Hours + lag1day,
     data=ride.Train)

reg5 <-
  lm(Trip_Count ~  start_station + factor(hour(interval60)) +  factor(dotw) + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday,
     data=ride.Train)
ride.Test.weekNest <-
  ride.Test %>%
  nest(-week)
model_pred <- function(dat, fit){
   pred <- predict(fit, newdata = dat)}
week_predictions <-
  ride.Test.weekNest %>%
    mutate(ATime_FE = map(.x = data, fit = reg1, .f = model_pred),
           BSpace_FE = map(.x = data, fit = reg2, .f = model_pred),
           CTime_Space_FE = map(.x = data, fit = reg3, .f = model_pred),
           DTime_Space_FE_timeLags = map(.x = data, fit = reg4, .f = model_pred),
           ETime_Space_FE_timeLags_holidayLags = map(.x = data, fit = reg5, .f = model_pred)) %>%
    gather(Regression, Prediction, -data, -week) %>%
    mutate(Observed = map(data, pull, Trip_Count),
           Absolute_Error = map2(Observed, Prediction,  ~ abs(.x - .y)),
           MAE = map_dbl(Absolute_Error, mean, na.rm = TRUE),
           sd_AE = map_dbl(Absolute_Error, sd, na.rm = TRUE))
week_predictions %>%
  select(week, Regression, MAE, sd_AE) %>%
  arrange(week, Regression) %>%
  kable(
    format = "html",
    digits = 2,
    col.names = c("Week", "Model", "Mean Absolute Error", "SD of Absolute Error"),
    caption = "Weekly MAE and Standard Deviation of Absolute Errors by Regression Model"
  ) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  ) %>%
  collapse_rows(
    columns = 1,
    valign = "top",
    row_group_label_position = "stack"
  )
Weekly MAE and Standard Deviation of Absolute Errors by Regression Model
Week Model Mean Absolute Error SD of Absolute Error
18 ATime_FE 0.73 0.83
BSpace_FE 0.74 0.89
CTime_Space_FE 0.73 0.82
DTime_Space_FE_timeLags 0.64 0.75
ETime_Space_FE_timeLags_holidayLags 0.64 0.75
19 ATime_FE 0.71 0.83
BSpace_FE 0.70 0.90
CTime_Space_FE 0.72 0.82
DTime_Space_FE_timeLags 0.61 0.75
ETime_Space_FE_timeLags_holidayLags 0.61 0.75
week_predictions %>%
  dplyr::select(week, Regression, MAE) %>%
  gather(Variable, MAE, -Regression, -week) %>%
  ggplot(aes(week, MAE)) +
    geom_bar(aes(fill = Regression), position = "dodge", stat="identity") +
    scale_fill_manual(values = palette5) +
    labs(title = "Mean Absolute Errors by model specification and week") +
  plotTheme

week_predictions %>%
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station)) %>%
    dplyr::select(interval60, start_station, Observed, Prediction, Regression) %>%
    unnest() %>%
    gather(Variable, Value, -Regression, -interval60, -start_station) %>%
    group_by(Regression, Variable, interval60) %>%
    summarize(Value = sum(Value)) %>%
    ggplot(aes(interval60, Value, colour=Variable)) +
      geom_line(size = 1.1) +
      facet_wrap(~Regression, ncol=1) +
      labs(title = "Predicted/Observed bike share time series", subtitle = "Philadelphia; A test set of 2 weeks",  x = "Hour", y= "Station Trips") +
        theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

week_predictions %>%
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station),
           start_lat = map(data, pull, start_lat),
           start_lon = map(data, pull, start_lon)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  labs(title="Mean Abs Error, Test Set, Model 5")+
  mapTheme

week_predictions %>%
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station),
           start_lat = map(data, pull, start_lat),
           start_lon = map(data, pull, start_lon),
           dotw = map(data, pull, dotw)) %>%
    select(interval60, start_station, start_lon,
           start_lat, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
  ggplot()+
  geom_point(aes(x= Observed, y = Prediction))+
    geom_smooth(aes(x= Observed, y= Prediction), method = "lm", se = FALSE, color = "red")+
    geom_abline(slope = 1, intercept = 0)+
  facet_grid(time_of_day~weekend)+
  labs(title="Observed vs Predicted",
       x="Observed trips",
       y="Predicted trips")+
  plotTheme

week_predictions %>%
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station),
           start_lat = map(data, pull, start_lat),
           start_lon = map(data, pull, start_lon),
           dotw = map(data, pull, dotw) ) %>%
    select(interval60, start_station, start_lon,
           start_lat, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush")) %>%
  group_by(start_station, weekend, time_of_day, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", size = 3, alpha = 0.4)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  facet_grid(weekend~time_of_day)+
  labs(title="Mean Absolute Errors, Test Set")+
  mapTheme

week_predictions %>%
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station),
           start_lat = map(data, pull, start_lat),
           start_lon = map(data, pull, start_lon),
           dotw = map(data, pull, dotw),
           Percent_Taking_Public_Trans = map(data, pull, Percent_Taking_Public_Trans),
           Med_Inc = map(data, pull, Med_Inc),
           Percent_White = map(data, pull, Percent_White)) %>%
    select(interval60, start_station, start_lon,
           start_lat, Observed, Prediction, Regression,
           dotw, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush")) %>%
  filter(time_of_day == "AM Rush") %>%
  group_by(start_station, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  gather(-start_station, -MAE, key = "variable", value = "value")%>%
  ggplot(.)+
  #geom_sf(data = chicagoCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = value, y = MAE), alpha = 0.4)+
  geom_smooth(aes(x = value, y = MAE), method = "lm", se= FALSE)+
  facet_wrap(~variable, scales = "free")+
  labs(title="Errors as a function of socio-economic variables",
       y="Mean Absolute Error (Trips)")+
    theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=8),
        axis.text.y=element_text(size=8), 
        axis.title=element_text(size=10), 
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

LS0tCnRpdGxlOiAiQXNzaWdubWVudCA1OiBTcGFjZS1UaW1lIFByZWRpY3Rpb24gb2YgQmlrZSBTaGFyZSBEZW1hbmQiCmF1dGhvcjogIlpoYW5jaGFvIFlhbmciCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRvYzogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkocmllbSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShSU29jcmF0YSkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KcGxvdFRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuawogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogIHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksCiAgIyBGb3JtYXQgdGhlIGdyaWQKICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNEMEQwRDAiLHNpemU9LjIpLAogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQoKbWFwVGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICAgICAgICAgIGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLAogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpCgpwYWxldHRlNSA8LSBjKCIjMTI0RTc4IiwiI0YwRjBDOSIsIiNGMkJCMDUiLCIjRDc0RTA5IiwiIzZFMEUwQSIpCnBhbGV0dGU0IDwtIGMoIiNEMkZCRDQiLCIjOTJCQ0FCIiwiIzUyN0Q4MiIsIiMxMjNGNUEiKQpwYWxldHRlMiA8LSBjKCIjNmJhZWQ2IiwiIzA4NTE5YyIpCmBgYAojIEludHJvZHVjdGlvbgoKIyMgQmlrZSBTaGFyZSBTeXN0ZW1zIFJvbGUgaW4gVXJiYW4gTW9iaWxpdGllcwoKQmlrZSBzaGFyZSBzeXN0ZW0gYXJlIG9uZSBvZiB0aGUgY3JpdGljYWwgY29tcG9uZW50cyBvZiB1cmJhbiBtb2JpbGl0eS4gVGhleSBwcm92aWRlIGEgZmxleGlibGUgYW5kIGNvbnZlbmllbnQgbW9kZSBvZiB0cmFuc3BvcnRhdGlvbiBmb3Igc2hvcnQgdHJpcHMsIHJlZHVjaW5nIGNvbmdlc3Rpb24gYW5kIHByb21vdGluZyBzdXN0YWluYWJsZSB0cmF2ZWwuIFRoZSBzeXN0ZW1zIGFyZSBkZXNpZ25lZCB0byBiZSB1c2VyLWZyaWVuZGx5LCB3aXRoIGVhc3kgYWNjZXNzIHRvIGJpa2VzIGFuZCBkb2NraW5nIHN0YXRpb25zLCBtYWtpbmcgdGhlbSBhbiBhdHRyYWN0aXZlIG9wdGlvbiBmb3IgY29tbXV0ZXJzIGFuZCB0b3VyaXN0cyBhbGlrZS4gQXQgdGhlIHNhbWUgdGltZSwgYmlrZSBzaGFyaW5nIHN5c3RlbXMgZmlsbCBhIGdhcCBpbiB0aGUgcHVibGljIHRyYW5zcG9ydGF0aW9uIHN5c3RlbSwgcHJvdmlkaW5nIGEgbGFzdC1taWxlIHNvbHV0aW9uIGZvciB1c2VycyB3aG8gbmVlZCB0byB0cmF2ZWwgc2hvcnQgZGlzdGFuY2VzLiBCeSBpbnRlZ3JhdGluZyBiaWtlIHNoYXJpbmcgd2l0aCBvdGhlciBtb2RlcyBvZiB0cmFuc3BvcnRhdGlvbiwgc3VjaCBhcyBidXNlcyBhbmQgdHJhaW5zLCBjaXRpZXMgY2FuIGNyZWF0ZSBhIG1vcmUgZWZmaWNpZW50IGFuZCBzdXN0YWluYWJsZSB0cmFuc3BvcnRhdGlvbiBuZXR3b3JrLgoKIyMgUmViYWxhbmNpbmcgUHJvYmxlbXMgaW4gQmlrZSBTaGFyaW5nIFN5c3RlbXMKCk9uZSBvZiB0aGUga2V5IG9wZXJhdGlvbmFsIGNoYWxsZW5nZXMgaW4gYmlrZS1zaGFyZSBzeXN0ZW0gaXMgdG8gbWFpbnRhaW4gYSBiYWxhbmNlZCBkaXN0cmlidXRpb24gYWNyb3NzIHN0YXRpb25zIGFuZCB0aW1lcyB0byBtZWV0IHVzZXIgZGVtYW5kcy4gU29tZSBzdGF0aW9ucyBmcmVxdWVudGx5IGV4cGVyaWVuY2UgYmlrZSBzaG9ydGFnZXMgd2hlcmUgdGhlIHVzZXJzIGNhbm5vdCBnZXQgYSBiaWtlIGZyb20gdGhlIGRvY2tzIHdoZW4gdGhleSBuZWVkIHRoZW0uIEhvd2V2ZXIsIHNvbWUgc3RhdGlvbnMgZnJlcXVlbnRseSBleHBlcmllbmNlIHN1cnBsdXNlcyB3aGVyZSB0aGUgdXNlcnMgY2Fubm90IHJldHVybiB0aGUgYmlrZXMgdG8gdGhlIGRvY2tzIHdoZW4gdGhleSBuZWVkIHRoZW0uIFRoaXMgcmViYWxhbmNlIGlzc3VlcyBiZWNvbWUgZXh0cmVtZWx5IHNlcmlvdXMgZHVyaW5nIHRoZSBwZWFrIGhvdXJzIGR1ZSB0byBhc3N5bWV0cmljIGNvbW11dGluZyBwYXR0ZXJucy4gV2l0aG91dCB0aW1lbHkgcmViYWxhbmNpbmcsIHVzZXJzIG1heSBmaW5kIGl0IGRpZmZpY3VsdCB0byByZW50IG9yIHJldHVybiBiaWtlcyBvbiB0aW1seSBtYW5uZXJzLCBsZWFkaW5nIHRvIHVzZXIgZGlzc2F0aXNmYWN0aW9uIGFuZCByZWR1Y2VkIHJpZGVyc2hpcC4gRWZmaWNpZW50IHJlYmxhbmNpbmcgYWhlYWQgb2YgdXNlciBkZW1hbmRzIGVuc3VyZXMgdGhlIHN5c3RlbSdzIGF2YWlsYWJpbGl0eSBhbmQgcmVsaWFiaWxpdHksIHdoaWNoIGFsc28gaW52b2x2ZXMgcmVhbC10aW1lIGRhdGEgYW5hbHlzaXMgYW5kIHByZWRpY3Rpb24uCgojIyBQcm9wb3NlZCBSZWJhbGFuY2luZyBTdHJhdGVnaWVzCgpUbyBhZGRyZXNzIHRoaXMgaXNzdWUsIHdlIHByb3Bvc2UgYSBkYXRhLWRyaXZlbiBhcHByb2FjaCB0byBwcmVkaWN0IHRoZSBiaWtlIGRlbWFuZCBhY3Jvc3MgZWFjaCBzdGF0aW9ucyBhbmQgdGltZSBpbnRlcnZhbHMuIEJ5IGFuYWx5emluZyBoaXN0b3JpY2FsIGRhdGEsIHdlIGNhbiBpZGVudGlmeSBwYXR0ZXJucyBhbmQgdHJlbmRzIGluIGJpa2UgdXNhZ2UsIGFsbG93aW5nIHVzIHRvIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zIGFib3V0IHdoZXJlIGFuZCB3aGVuIHRvIHJlZGlzdHJpYnV0ZSBiaWtlcy4gVGhpcyBhcHByb2FjaCBub3Qgb25seSBpbXByb3ZlcyB0aGUgZWZmaWNpZW5jeSBvZiB0aGUgYmlrZS1zaGFyZSBzeXN0ZW0gYnV0IGFsc28gZW5oYW5jZXMgdGhlIG92ZXJhbGwgdXNlciBleHBlcmllbmNlIGJ5IGVuc3VyaW5nIHRoYXQgYmlrZXMgYXJlIGF2YWlsYWJsZSB3aGVuIGFuZCB3aGVyZSB0aGV5IGFyZSBuZWVkZWQuIFByZWRpY3Rpb25zIHdpbGwgYmUgbWFkZSBzZXZlcmFsIGhvdXJzIGluIGFkdmFuY2UgdXNpbmcgdGhlIG1vZGVscyB0aGF0IGluY29ycG9yYXRlIGZlYXR1cmVzIHN1Y2ggYXMgdGltZSBvZiBkYXksIGRheSBvZiB3ZWVrLCBoaXN0b3JpY2FsIHVzYWdlIHBhdHRlcm5zLCB3ZWF0aGVyIGNvbmRpdGlvbnMsIGFuZCBjdXJyZW50IHN0YXRpb24gY2FwYWNpdHkuIEJ5IGxldmVyYWdpbmcgdGhlc2UgZGF0YSBzb3VyY2VzLCB3ZSBjYW4gb3B0aW1pemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBiaWtlcyBhY3Jvc3MgdGhlIHN5c3RlbSwgcmVkdWNpbmcgdGhlIGxpa2VsaWhvb2Qgb2Ygc2hvcnRhZ2VzIGFuZCBzdXJwbHVzZXMuCgpUaGUgbW9kZWwgd2lsbCBiZSBmb2N1c2VkIHJlYmFsYW5jZSBlZmZvcnRzIG9uIHRoZSBzdGF0aW9ucyBpbiBQaGlsYWRlbHBoaWEsIFBBLiBUaGUgbW9kZWwgd2lsbCBiZSB0cmFpbmVkIHVzaW5nIHRoZSBoaXN0b3JpY2FsIGJpa2Ugc2hhcmUgZGF0YSBmb3IgdGhlIG1vbnRoIG9mIE1heSwgMjAyMi4gVGhlIG1vZGVsIHdpbGwgYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBiaWtlIGRlbWFuZCBmb3IgdGhlIG5leHQgMjQgaG91cnMsIGFuZCB0aGUgcmVzdWx0cyB3aWxsIGJlIHVzZWQgdG8gaW5mb3JtIHJlYmFsYW5jaW5nIGVmZm9ydHMuCgojIERhdGEgUHJlcHJvY2Vzc2luZyBhbmQgRmVhdHVyZSBFbmdpbmVlcmluZwoKIyMgRGF0YSBQcmVwYXJhdGlvbgoKIyMjIEJpa2UgZGF0YQoKVGhlIGJpa2Ugc2hhcmUgZGF0YSBpcyBvYnRhaW5lZCBmcm9tIHRoZSBJbmRlZ28uIFRoZSBkYXRhIGluY2x1ZGVzIGluZm9ybWF0aW9uIG9uIGJpa2UgdHJpcHMsIGluY2x1ZGluZyBzdGFydCBhbmQgZW5kIHRpbWVzLCBzdGFydCBhbmQgZW5kIHN0YXRpb25zLCBhbmQgYmlrZSBJRHMuIFRoZSBkYXRhIGlzIGNsZWFuZWQgYW5kIHByZXByb2Nlc3NlZCB0byByZW1vdmUgYW55IGR1cGxpY2F0ZXMgb3IgbWlzc2luZyB2YWx1ZXMuIFRoZSBkYXRhIGlzIHRoZW4gYWdncmVnYXRlZCB0byBob3VybHkgaW50ZXJ2YWxzIHRvIGNyZWF0ZSBhIHRpbWUgc2VyaWVzIG9mIGJpa2UgZGVtYW5kIGZvciBlYWNoIHN0YXRpb24uIE1heSAyMDIyIGRhdGEgd2FzIHNlbGVjdGVkIGZvciB0aGlzIGFuYWx5c2lzIGFzIHRoZSB0ZW1wb3JhbCBmcmFtZS4gCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYmlrZTwtcmVhZC5jc3YoImRhdGEvaW5kZWdvLXRyaXBzLTIwMjItcTIuY3N2IikKCgptYXkgPC0gYmlrZSAlPiUKICBtdXRhdGUoc3RhcnRfdGltZSA9IG1keV9obShzdGFydF90aW1lKSkgJT4lCiAgZmlsdGVyKG1vbnRoKHN0YXJ0X3RpbWUpID09IDUpICU+JQogIG11dGF0ZShpbnRlcnZhbDYwID0gZmxvb3JfZGF0ZSh5bWRfaG1zKHN0YXJ0X3RpbWUpLCB1bml0ID0gImhvdXIiKSwKICAgICAgICAgaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzdGFydF90aW1lKSwgdW5pdCA9ICIxNSBtaW5zIiksCiAgICAgICAgIHdlZWsgPSB3ZWVrKGludGVydmFsNjApLAogICAgICAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwLCBsYWJlbD1UUlVFKSkKCm1heTwtbWF5JT4lCiAgZmlsdGVyKCFpcy5uYSh5bWRfaG1zKHN0YXJ0X3RpbWUpKSkKYGBgCgojIyMgUG9wdWxhdGlvbiBkYXRhCgpBdCB0aGUgc2FtZSB0aW1lLCBjZW5zdXMgdHJhY3QgZ2VvbWV0cmllcyBvZiB0aGUgZW50aXJlIFBoaWxhZGVscGhpYSBjb3VudHkgd2FzIGFjcXVpcmVkIGZyb20gdGhlIFVTIENlbnN1cyBCdXJlYXUgdG8gcHJvdmlkZSB0aGUgZ2VvZ3JhcGhpYyB6b25lIGJvdW5kYXJpZXMgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgaWYgbmVlZGVkLiAKCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGhpbENlbnN1cyA8LQogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwKICAgICAgICAgIHZhcmlhYmxlcyA9IGMoIkIwMTAwM18wMDEiLCAiQjE5MDEzXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIwODAxM18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjA4MDEyXzAwMSIsICJCMDgzMDFfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIwODMwMV8wMTAiLCAiQjAxMDAyXzAwMSIpLAogICAgICAgICAgeWVhciA9IDIwMjIsCiAgICAgICAgICBzdGF0ZSA9ICJQQSIsCiAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsCiAgICAgICAgICBjb3VudHk9YygiUGhpbGFkZWxwaGlhIiksCiAgICAgICAgICBvdXRwdXQgPSAid2lkZSIpICU+JQogIHJlbmFtZShUb3RhbF9Qb3AgPSAgQjAxMDAzXzAwMUUsCiAgICAgICAgIE1lZF9JbmMgPSBCMTkwMTNfMDAxRSwKICAgICAgICAgTWVkX0FnZSA9IEIwMTAwMl8wMDFFLAogICAgICAgICBXaGl0ZV9Qb3AgPSBCMDIwMDFfMDAyRSwKICAgICAgICAgVHJhdmVsX1RpbWUgPSBCMDgwMTNfMDAxRSwKICAgICAgICAgTnVtX0NvbW11dGVycyA9IEIwODAxMl8wMDFFLAogICAgICAgICBNZWFuc19vZl9UcmFuc3BvcnQgPSBCMDgzMDFfMDAxRSwKICAgICAgICAgVG90YWxfUHVibGljX1RyYW5zID0gQjA4MzAxXzAxMEUpICU+JQogIHNlbGVjdChUb3RhbF9Qb3AsIE1lZF9JbmMsIFdoaXRlX1BvcCwgVHJhdmVsX1RpbWUsCiAgICAgICAgIE1lYW5zX29mX1RyYW5zcG9ydCwgVG90YWxfUHVibGljX1RyYW5zLAogICAgICAgICBNZWRfQWdlLAogICAgICAgICBHRU9JRCwgZ2VvbWV0cnkpICU+JQogIG11dGF0ZShQZXJjZW50X1doaXRlID0gV2hpdGVfUG9wIC8gVG90YWxfUG9wLAogICAgICAgICBNZWFuX0NvbW11dGVfVGltZSA9IFRyYXZlbF9UaW1lIC8gVG90YWxfUHVibGljX1RyYW5zLAogICAgICAgICBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMgPSBUb3RhbF9QdWJsaWNfVHJhbnMgLyBNZWFuc19vZl9UcmFuc3BvcnQpCgpwaGlsVHJhY3RzIDwtCiAgcGhpbENlbnN1cyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZGlzdGluY3QoR0VPSUQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIHNlbGVjdChHRU9JRCwgZ2VvbWV0cnkpICU+JQogIHN0X3NmCmBgYAoKU2luY2UgdGhlIHJhdyBiaWtlc2hhcmUgZGF0YSBpcyBhIG5vbi1zcGF0aWFsIGxheWVyLCBvbmx5IGNvbnRhaW5pbmcgbGF0aXR1dGUgYW5kIGxvbmdpdHVkZSBvZiB0aGUgc3RhcnQgYW5kIGVuZCBzdGF0aW9ucywgd2UgbmVlZCB0byBqb2luIHRoZSBiaWtlc2hhcmUgZGF0YSB3aXRoIHRoZSBjZW5zdXMgdHJhY3QgZGF0YSB0byBvYnRhaW4gdGhlIGNlbnN1cyB0cmFjdCBpbmZvcm1hdGlvbiBmb3IgZWFjaCB0cmlwLiBUaGUgYHN0X2pvaW5gIGZ1bmN0aW9uIGZyb20gdGhlIGBzZmAgcGFja2FnZSBpcyB1c2VkIHRvIHBlcmZvcm0gYSBzcGF0aWFsIGpvaW4gYmV0d2VlbiB0aGUgYmlrZXNoYXJlIGRhdGEgYW5kIHRoZSBjZW5zdXMgdHJhY3QgZGF0YS4gV2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiB0aGUgc3RhcnQgbG9jYXRpb24gb2YgZWFjaCByaWRlIGJlY2F1c2Ugd2Ugd2FudCB0byBwcmVkaWN0IHRoZSBjYXBhY2l0eSBvZiBlYWNoIHN0YXRpb24gd2hlbiB0aGUgdXNlciBuZWVkIGEgYmlrZSB0byBzdGFydCB0aGUgcmlkZS4gVGhpcyByZXF1aXJlcyB1cyB0byBmaWx0ZXIgb3V0IHJvd3Mgd2l0aCBtaXNzaW5nIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuIApgYGB7cn0KZGF0X2NlbnN1cyA8LSBzdF9qb2luKG1heSAlPiUKICAgICAgICAgIGZpbHRlcihpcy5uYShzdGFydF9sb24pID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgIGlzLm5hKHN0YXJ0X2xhdCkgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgaXMubmEoZW5kX2xvbikgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgaXMubmEoZW5kX2xhdCkgPT0gRkFMU0UpICU+JQogICAgICAgICAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygic3RhcnRfbG9uIiwgInN0YXJ0X2xhdCIpLCBjcnMgPSA0MzI2KSwKICAgICAgICBwaGlsVHJhY3RzICU+JQogICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSwKICAgICAgICBqb2luPXN0X2ludGVyc2VjdHMsCiAgICAgICAgICAgICAgbGVmdCA9IFRSVUUpICU+JQogIHJlbmFtZShPcmlnaW4uVHJhY3QgPSBHRU9JRCkgJT4lCiAgbXV0YXRlKHN0YXJ0X2xvbiA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDEpKSwKICAgICAgICAgc3RhcnRfbGF0ID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMikpKSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzZWxlY3QoLWdlb21ldHJ5KSU+JQogIHN0X2FzX3NmKC4sIGNvb3JkcyA9IGMoImVuZF9sb24iLCAiZW5kX2xhdCIpLCBjcnMgPSA0MzI2KSAlPiUKICBzdF9qb2luKC4sIHBoaWxUcmFjdHMgJT4lCiAgICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnM9NDMyNiksCiAgICAgICAgICBqb2luPXN0X2ludGVyc2VjdHMsCiAgICAgICAgICBsZWZ0ID0gVFJVRSkgJT4lCiAgcmVuYW1lKERlc3RpbmF0aW9uLlRyYWN0ID0gR0VPSUQpICAlPiUKICBtdXRhdGUodG9fbG9uZ2l0dWRlID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMSkpLAogICAgICAgICB0b19sYXRpdHVkZSA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDIpKSklPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgc2VsZWN0KC1nZW9tZXRyeSkKYGBgCgpUaGUgZm9sbG93aW5nIG1hcCBzaG93cyBhbGwgdGhlIHN0YXJ0aW5nIHBvaW50IG9mIHRoZSByaWRlcyBpbiBQaGlsYWRlbHBoaWEgZHVyaW5nIE1heSAyMDIyLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KZ2dwbG90KCkrCiAgZ2VvbV9zZihkYXRhID0gcGhpbFRyYWN0cyAlPiUKICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnM9NDMyNiksIGZpbGw9IndoaXRlIikrCiAgZ2VvbV9wb2ludChkYXRhID0gbWF5LCBhZXMoeD1zdGFydF9sb24sIHkgPSBzdGFydF9sYXQpLCAKICAgICAgICAgICAgY29sb3IgPSAiIzZFMEUwQSIsIGFscGhhID0gMSwgc2l6ZSA9IDAuMykgKyAKICB5bGltKG1pbihtYXkkc3RhcnRfbGF0KSwgbWF4KG1heSRzdGFydF9sYXQpKSsKICB4bGltKG1pbihtYXkkc3RhcnRfbG9uKSwgbWF4KG1heSRzdGFydF9sb24pKSArCiAgbGFicyh0aXRsZSA9ICJMb2NhdGlvbiBvZiBSaWRlcyBTdGFydGluZyBQb2ludHMgaW4gUGhpbGFkZWxwaGlhIikrCiAgbWFwVGhlbWUKYGBgCiMjIyBXZWF0aGVyIGRhdGEKVGhlIHdlYXRoZXIgZGF0YSBpcyBvYnRhaW5lZCBmcm9tIHRoZSBOYXRpb25hbCBXZWF0aGVyIFNlcnZpY2UgKFBoaWxhZGVscGhpYSBJbnRlcm5hdGlvbmFsIEFpcnBvcnQpLiBUaGUgZGF0YSBpbmNsdWRlcyBpbmZvcm1hdGlvbiBvbiB0ZW1wZXJhdHVyZSwgcHJlY2lwaXRhdGlvbiwgYW5kIHdpbmQgc3BlZWQuIFRoZSBkYXRhIGlzIGNsZWFuZWQgYW5kIHByZXByb2Nlc3NlZCB0byByZW1vdmUgYW55IGR1cGxpY2F0ZXMgb3IgbWlzc2luZyB2YWx1ZXMuIFRoZSBkYXRhIGlzIHRoZW4gYWdncmVnYXRlZCB0byBob3VybHkgaW50ZXJ2YWxzIHRvIGNyZWF0ZSBhIHRpbWUgc2VyaWVzIG9mIHdlYXRoZXIgY29uZGl0aW9ucyBmb3IgZWFjaCBzdGF0aW9uLgoKYGBge3J9CndlYXRoZXIuUGFuZWwgPC0KICByaWVtX21lYXN1cmVzKHN0YXRpb24gPSAiUEhMIiwgZGF0ZV9zdGFydCA9ICIyMDIyLTA1LTAxIiwgZGF0ZV9lbmQgPSAiMjAyMi0wNS0zMSIpICU+JQogIGRwbHlyOjpzZWxlY3QodmFsaWQsIHRtcGYsIHAwMWksIHNrbnQpJT4lCiAgcmVwbGFjZShpcy5uYSguKSwgMCkgJT4lCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IHltZF9oKHN1YnN0cih2YWxpZCwxLDEzKSkpICU+JQogICAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApLAogICAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsPVRSVUUpKSAlPiUKICAgIGdyb3VwX2J5KGludGVydmFsNjApICU+JQogICAgc3VtbWFyaXplKFRlbXBlcmF0dXJlID0gbWF4KHRtcGYpLAogICAgICAgICAgICAgIFByZWNpcGl0YXRpb24gPSBzdW0ocDAxaSksCiAgICAgICAgICAgICAgV2luZF9TcGVlZCA9IG1heChza250KSkgJT4lCiAgICBtdXRhdGUoVGVtcGVyYXR1cmUgPSBpZmVsc2UoVGVtcGVyYXR1cmUgPT0gMCwgNDIsIFRlbXBlcmF0dXJlKSkKYGBgCgojIyBGZWF0dXJlIEVuZ2luZWVyaW5nICYgRXhwbG9yYXRvcnkgQW5hbHlzaXMKCiMjIyBUZW1wb3JhbCBBbmFseXNpcwoKU3RhcnRpbmcgd2l0aCB0ZW1wb3JhbCBhbmFseXNpcywgd2UgZmlyc3QgdmlzdWFsaXplZCB0aGUgYmlrZXNoYXJlIHRyaXBzIHBlciBob3VyIGluIFBoaWxhZGVscGhpYSBpbiBNYXkgMjAyMy4gSXQgaXMgY2xlYXIgdGhhdCB0aGVyZSdzIHNvbWUgZm9ybSBvZiBjaXJjdWxhcml0eSBpbiB0aGUgZGF0YSBhdCBib3RoIHBlYWsgaG91cnMgYW5kIG5vbi1wZWFrIGhvdXJzIGR1cmluZyBzaW5nbGUgZGF5LiBXZSBtYXkgYWxzbyBzZWUgdGhhdCBhZnRlciBmaXZlIG51bWJlciBvZiBoaWdoIHBlYWtzIGFyZSB1c3VhbGx5IGJ5IHR3byBsb3dlciBwZWFrcyAod2Vla2RheSBhbmQgd2Vla2VuZCkuIFRoZXNlIGNvcnJlc3BvbmQgdGhlIHRyZW5kcyBtZW50aW9uZWQgaW4gdGhlIGludHJvZHVjdGlvbiBzZWN0aW9uIHRoYXQgYmlrZXNoYXJlIGFyZSBtdWNoIG1vcmUgbmVlZGVkIGR1cmluZyB0aGUgd2Vla2RheXMgcnVzaCBob3VycyBjb21wYXJlZCB0byB0aGUgd2Vla2VuZHMgYW5kIGxhdGUgbmlnaHQgaG91cnMuCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdF9jZW5zdXMgJT4lCiAgICAgICAgIGdyb3VwX2J5KGludGVydmFsNjApICU+JQogICAgICAgICB0YWxseSgpKSsKICBnZW9tX2xpbmUoYWVzKHggPSBpbnRlcnZhbDYwLCB5ID0gbikpKwogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMgcGVyIGhyLiBQaGlsYWRlbHBoaWEsIE1heSwgMjAyMiIsCiAgICAgICB4PSJEYXRlIiwKICAgICAgIHk9Ik51bWJlciBvZiB0cmlwcyIpKwogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKYGBgClRoZW4sIHdlIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRyaXBzIGJ5IHN0YXRpb25zIGF0IGVhY2ggaG91ciBpbiBNYXkuIFRoZSBkaXN0cmlidXRpb24gaXMgcmlnaHQgc2tld2VkLCB3aGVyZSBtb3N0IHN0YXRpb25zIGhhdmUgemVybyBvciB2ZXJ5IGZldyBudW1iZXIgb2YgdHJpcHMgZWFjaCBob3VyLiBIb3dldmVyLCB0aGVyZSBhcmUgZmV3IHN0YXRpb25zIHRoYXQgaGF2ZSBhIHZlcnkgaGlnaCBudW1iZXIgb2YgdHJpcHMgZWFjaCBob3VyIG9uIHBhcnRpY3VsYXIgZGF5IGluIE1heSAyMDIyLiAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUKICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbikgJT4lCiAgICAgICAgIHRhbGx5KCkpKwogIGdlb21faGlzdG9ncmFtKGFlcyhuKSwgYmlud2lkdGggPSA1LCBmaWxsPSIjNkUwRTBBIikrCiAgbGFicyh0aXRsZT0iQmlrZSBzaGFyZSB0cmlwcyBwZXIgaG91ciBieSBzdGF0aW9uLiBQaGlsYWRlbHBoaWEsIE1heSwgMjAyMiIsCiAgICAgICB4PSJUcmlwIENvdW50cyIsCiAgICAgICB5PSJOdW1iZXIgb2YgU3RhdGlvbnMiKSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyMjIFRlbXBvcmFyeSBGZWF0dXJlIEVuZ2luZWVyCgpXZSBwZXJmb3JtIGZlYXR1cmUgZW5naW5lZXJpbmcgdXNpbmcgYGx1YnJ1ZGF0ZWAgcGFja2FnZSB0byBleHRyYWN0IHRlbXBvcmFsIGluZm9ybWF0aW9uIGZvciBlYWNoIHVzYWdlLiB0aGUgdXNhZ2Ugd2FzIGNhbGN1bGF0aW5nIGludG8gNjAgbWluIGFuZCAxNSBtaW4gaW50ZXJ2YWwgYmFzZWQgb24gc3RhcmluZyB0aW1lIGZvciBlYWNoIHJpZGUuIEFuZCB0aGVuLCB0aGUgcmlkZSB3YXMgZnVydGhlciBjYXRlZ29yaXplIGludG8gIk92ZXJuaWdodCIsICJBTSBSdXNoIiwgIk1pZC1EYXkiIGFuZCAiUE0gUnVzaCIgYmFzZWQgb24gdGhlIGhvdXIgb2YgdGhlIGRheSBhbmQgaW50byB3ZWVrZGF5IGFuZCB3ZWVrZW5kIGJhc2VkIG9uIHRoZSBkYXkgb2YgdGhlIHdlZWsuCgpXZSB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWVhbiBudW1iZXIgdHJpcHMgYXQgZWFjaCBzdGF0aW9uIGR1cmluZyBkaWZmZXJlbnQgdGltZSBpbnRlcnZhbHMgb2YgdGhlIGRheS4gRHVyaW5nIHRoZSBQTSBydXNoIGhvdXIsIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0cmlwcyBpcyB0aGUgaGlnaGVzdCwgZm9sbG93ZWQgYnkgdGhlIG1pZC1kYXkgYW5kIEFNIHJ1c2ggaG91ci4gVGhlIG92ZXJuaWdodCBwZXJpb2QgaGFzIHRoZSBsb3dlc3QgYXZlcmFnZSBudW1iZXIgb2YgdHJpcHMsIHdpdGggb25seSBmZXcgc3RhdGlvbiB3aXRoIGFuIGF2ZXJhZ2Ugb2YgMyB0cmlwcyBvciBtb3JlLiAKCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KcGFsZXR0ZTQgPC0gYygiIzEyNEU3OCIsIiNGMEYwQzkiLCIjRjJCQjA1IiwiIzZFMEUwQSIpCmRhdF9jZW5zdXMgJT4lCiAgICAgICAgbXV0YXRlKHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSU+JQogICAgICAgICBncm91cF9ieShpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCB0aW1lX29mX2RheSkgJT4lCiAgICAgICAgIHRhbGx5KCklPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uLCB0aW1lX29mX2RheSklPiUKICBzdW1tYXJpemUobWVhbl90cmlwcyA9IG1lYW4obikpJT4lCiAgZ2dwbG90KCkrCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKG1lYW5fdHJpcHMsIGZpbGw9dGltZV9vZl9kYXkpLCBiaW53aWR0aCA9IDEpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU0LCBuYW1lPSJUaW1lIG9mIERheSIpICsKICBsYWJzKHRpdGxlPSJNZWFuIE51bWJlciBvZiBIb3VybHkgVHJpcHMgUGVyIFN0YXRpb24uIFBoaWxhZGVscGhpYSwgTWF5LCAyMDIyIiwKICAgICAgIHg9Ik51bWJlciBvZiB0cmlwcyIsCiAgICAgICB5PSJGcmVxdWVuY3kiKSsKICBmYWNldF93cmFwKH50aW1lX29mX2RheSkrCiAgICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKCkFmdGVyIHRoYXQsIHdlIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZWFuIG51bWJlciBvZiB0cmlwcyBhdCBlYWNoIHN0YXRpb24gZHVyaW5nIGRpZmZlcmVudCBkYXlzIG9mIHRoZSB3ZWVrLiBGcm9tIHRoZSBjaGFydCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBNb25kYXksIFR1ZXNkYXksIFdlZG5lc2RheSwgYW5kIFRodXJzZGF5IHNob3dzIHNpbWlsYXIgdGVtcG9yYWwgcGF0dGVybnMsIHdpdGggc2ltaWxhciBhbmQgaGlnaGVzdCBudW1iZXIgb2YgdHJpcHMgYnkgdGhlIHRpbWUgb2YgdGhlIGRheS4gVGhlIEZyaWRheSBhbmQgU3R1cmRheSBzaG93cyBzaW1pbGFyIHBhdHRlcm5zLCB3aXRoIHNtYWxsZXIgbnVtYmVyIG9mIHBlb3BsZSB1dGlsaXplIHRoZSBzeXN0ZW0uIEFuZCwgU3VuZGF5IHNob3dzIGEgdW5pcXVlcGVhayBwYXR0ZXJuIHRoYW4gYW55IG90aGVyIHdlZWtkYXlzIG9yIHdlZWtlbmRzLkJ1dCBpbiBnZW5lcmFsLCBsb3dlc3QgdHJpcCBjb3VudCBvY2N1ciBpbiB0aGUgZWFybHkgbW9ybmluZyB3aGlsZSBoaWdoZXN0IHRyaXAgY291bnQgb2NjdXIgaW4gdGhlIGxhdGUgYWZ0ZXJub29uLiAKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRfY2Vuc3VzICU+JSBtdXRhdGUoaG91ciA9IGhvdXIoc3RhcnRfdGltZSkpKSsKICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhob3VyLCBjb2xvciA9IGRvdHcpLCBiaW53aWR0aCA9IDMsbHdkPTAuOCkrCiAgbGFicyh0aXRsZT0iQmlrZSBzaGFyZSB0cmlwcyBpbiBQaGlsYWRlbHBoaWEsIGJ5IGRheSBvZiB0aGUgd2VlaywgTWF5LCAyMDIyIiwKICAgICAgIHg9IkhvdXIiLAogICAgICAgeT0iVHJpcCBDb3VudHMiKSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYApXZSBhbHNvIGNvbXBhcmVkIHRoZSBkaWZmZXJlbnQgdXNhZ2UgYmV0d2VlbiB3ZWVrZGF5cyBhbmQgd2Vla2VuZHMuIFRoZSBjaGFydCBzaG93cyB0aGF0IHRoZSB3ZWVrZGF5IHVzYWdlIGlzIG11Y2ggaGlnaGVyIHRoYW4gdGhlIHdlZWtlbmQgdXNhZ2UuIFRoZSB3ZWVrZGF5IHVzYWdlIHNob3dzIGEgc21hbGxlciBwZWFrIGR1cmluZyB0aGUgQU0gcnVzaCBob3VyIGFuZCBhbiBldmVuIGJpZ2dlciBwZWFrIGR1cmluZyB0aGUgUE0gcnVzaCBob3VyLiBUaGUgd2Vla2VuZCB1c2FnZSBzaG93cyBhIHNtYWxsZXIgcGVhayBkdXJpbmcgdGhlIGxhdGUgbW9ybmluZyBhbmQgZWFybHkgYWZ0ZXJub29uLCB3aXRoIGEgZ3JhZHVhbCBkZWNsaW5lIGluIHVzYWdlIHRocm91Z2hvdXQgdGhlIGRheS4KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9CmdncGxvdChkYXRfY2Vuc3VzICU+JQogICAgICAgICBtdXRhdGUoaG91ciA9IGhvdXIoc3RhcnRfdGltZSksCiAgICAgICAgICAgICAgICB3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSkpKwogICAgIGdlb21fZnJlcXBvbHkoYWVzKGhvdXIsIGNvbG9yID0gd2Vla2VuZCksIGJpbndpZHRoID0gMSkrCiAgbGFicyh0aXRsZT0iQmlrZSBzaGFyZSB0cmlwcyBpbiBQaGlsYWRlbHBoaWEgLSB3ZWVrZW5kIHZzIHdlZWtkYXksIE1heSwgMjAyMiIsCiAgICAgICB4PSJIb3VyIiwKICAgICAgIHk9IlRyaXAgQ291bnRzIikrCiAgICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKCiMjIyBTcGF0aWFsIGFuZCBUZW1wb3JhbCBBbmFseXNpcwpXZSB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdHJpcHMgY291bnQgYXQgZWFjaCBzdGF0aW9uIGR1cmluZyBkaWZmZXJlbnQgdGltZSBpbnRlcnZhbHMgb24gd2Vla2RheSBhbmQgd2Vla2VuZC4gQXMgdGhlIG1hcCBzaG93cyBiZWxvdywgaXQgaW5kaWNhdGUgdGhhdCB0aGUgd2Vla2RheSB1c2FnZSBpcyBtdWNoIGhpZ2hlciB0aGFuIHRoZSB3ZWVrZW5kIHVzYWdlLiBPbiBib3RoIHdlZWtkYXkgYW5kIHdlZWtlbmQsIG1vc3Qgb2YgdGhlIHRyaXAgc3RhcnRzIHN0YXRpb24gY2x1c3RlcmVkIGFyb3VuZCB0aGUgY2VudHJhbCBjaXR5IFBoaWxhZGVscGhpYSBhcmVhLCB3aXRoIGEgZmV3IHRyaXBzIHN0YXJ0cyBpbiB0aGUgd2VzdCBQaGlsYWRlbHBoaWEsIFNvdXRoIFBoaWxhZGVscGhpYS4gVGhlc2UgZGlzdHJpYnV0aW9uIHN1Z2dlc3RzIHRoYXQgdHJpcCBkZW1hbmRzIGlzIGVzcGVjaWFsbHkgaGlnaGVyIGluIHRoZSBjZW50cmFsIGNpdHkgYXJlYSBhbmQgbWFqb3IgdHJhbnNwb3J0YXRpb24gZmFjaWxpdGllcy4gVGhlcmUgaXMgcHJvYmFibHkgbmVlZCB0byByZWJhbGFuY2UgdGhlIGJpa2VzIGluIHRoZXNlIGFyZWFzIHRvIG1lZXQgdGhlIGRlbWFuZC4KCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdCgpKwogIGdlb21fc2YoZGF0YSA9IHBoaWxUcmFjdHMgJT4lCiAgICAgICAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpKSsKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRfY2Vuc3VzICU+JQogICAgICAgICAgICBtdXRhdGUoaG91ciA9IGhvdXIoc3RhcnRfdGltZSksCiAgICAgICAgICAgICAgICB3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSwKICAgICAgICAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xhdCwgc3RhcnRfbG9uLCB3ZWVrZW5kLCB0aW1lX29mX2RheSkgJT4lCiAgICAgICAgICAgICAgdGFsbHkoKSwKICAgICAgICAgICAgYWVzKHg9c3RhcnRfbG9uLCB5ID0gc3RhcnRfbGF0LCBjb2xvciA9IG4pLAogICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgYWxwaGEgPSAwLjQsIHNpemUgPSAzKSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBmYWNldF9ncmlkKHdlZWtlbmQgfiB0aW1lX29mX2RheSkrCiAgbGFicyh0aXRsZT0iQmlrZSBzaGFyZSB0cmlwcyBwZXIgaHIgYnkgc3RhdGlvbi4gUGhpbGFkZWxwaGlhLCBNYXksIDIwMjIiKSsKICBtYXBUaGVtZQpgYGAKCgojIyMgV2VhdGhlciBBbmFseXNpcwoKTWF5IGluIFBoaWxhZGVscGhpYSB3YXMgY2hhcmFjdGVyaXplZCBieSBzdGVhZGlseSB3YXJtaW5nIHNwcmluZyBkYXlz4oCUbW9ybmluZyBsb3dzIGluIHRoZSA1MHMgwrBGIGNsaW1iaW5nIHRvIGhpZ2hzIGluIHRoZSBtaWQtODBzIGJ5IG1vbnRo4oCZcyBlbmTigJR3aXRoIHJlZ3VsYXIgMTXigJMyMCDCsEYuIFBlcmNpcGl0YXRpb24gd2VyZSBkIG1vc3RseSBsaWdodCB0YnV0IHdpdGggYSBmZXcgaW50ZW5zZSBkb3ducG91cnMgYXQgdGhlIGVuZCBvZiB0aGUgbW9udGguIFdpbmRzIGdlbmVyYWxseSBob3ZlcmVkIGFyb3VuZCA14oCTMTUgbXBoLiBUaGVzZSBicmllZiB3ZXQgYW5kIGJsdXN0ZXJ5IGludGVybHVkZXMgcHVuY3R1YXRlZCBvdGhlcndpc2UgbWlsZCwgY29tZm9ydGFibGUgd2VhdGhlciB0aHJvdWdob3V0IE1heS4KCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpncmlkLmFycmFuZ2UoCiAgZ2dwbG90KHdlYXRoZXIuUGFuZWwsIGFlcyhpbnRlcnZhbDYwLFByZWNpcGl0YXRpb24pKSArIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlPSJQZXJjaXBpdGF0aW9uIiwgeD0iSG91ciIsIHk9IlBlcmVjaXBpdGF0aW9uIikgKyB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKSwKICBnZ3Bsb3Qod2VhdGhlci5QYW5lbCwgYWVzKGludGVydmFsNjAsV2luZF9TcGVlZCkpICsgZ2VvbV9saW5lKCkgKwogICAgbGFicyh0aXRsZT0iV2luZCBTcGVlZCIsIHg9IkhvdXIiLCB5PSJXaW5kIFNwZWVkIikgK3RoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpLAogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxUZW1wZXJhdHVyZSkpICsgZ2VvbV9saW5lKCkgKwogICAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUiLCB4PSJIb3VyIiwgeT0iVGVtcGVyYXR1cmUiKSArdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSksCiAgdG9wPSJXZWF0aGVyIERhdGEgLSBQaGlsYWRlbHBoaWEgKFBITCkgLSBNYXksIDIwMjIiKQpgYGAKCgojIyBTcGFjZS1UaW1lIFBhbmVsCldlIGNyZWF0ZWQgYSBzdHVkeSBwYW5lbCB3aGVyZSBlYWNoIGluc3RhbmNlIGluIHRoZSBwYW5lbCBpcyBhIHVuaXF1ZSBjb21iaW5hdGlvbiBvZiBzcGFjZSBhbmQgdGltZS4gSW4gb3RoZXIgd29yZHMsIGVhY2ggcm93IHdpbGwgcmVwcmVzZW50IHRoZSByaWRlIGF0IGEgcGFydGljdWxhciBzdGF0aW9uIGR1cmluZyBhIHBhcnRpY3VsYXIgaG91ci4gQWZ0ZXIgdGhhdCwgd2UgYWRkIHNvbWUgbW9yZSBpbmZvcm1hdGlvbiB0byB0aGlzIHBhbmVsLiBUaGlzIGluY2x1ZGVzIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgcmlkZXMgYXQgdGhpcyBzdGF0aW9uIGF0IHRoaXMgcGFydGljdWxhciBob3VyLCBhZGRpbmcgd2VhdGhlciBpbmZvcm1hdGlvbiwgYW5kIGJyaW5naW5nIGluIGNlbnN1cyBkYXRhLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN0dWR5LnBhbmVsIDwtCiAgZXhwYW5kLmdyaWQoaW50ZXJ2YWw2MD11bmlxdWUoZGF0X2NlbnN1cyRpbnRlcnZhbDYwKSwKICAgICAgICAgICAgICBzdGFydF9zdGF0aW9uID0gdW5pcXVlKGRhdF9jZW5zdXMkc3RhcnRfc3RhdGlvbikpICU+JQogIGxlZnRfam9pbiguLCBkYXRfY2Vuc3VzICU+JQogICAgICAgICAgICAgIHNlbGVjdChzdGFydF9zdGF0aW9uLCBPcmlnaW4uVHJhY3QsIHN0YXJ0X2xvbiwgc3RhcnRfbGF0ICklPiUKICAgICAgICAgICAgICBkaXN0aW5jdCgpICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24pICU+JQogICAgICAgICAgICAgIHNsaWNlKDEpKQoKcmlkZS5wYW5lbCA8LQogIGRhdF9jZW5zdXMgJT4lCiAgbXV0YXRlKFRyaXBfQ291bnRlciA9IDEpICU+JQogIHJpZ2h0X2pvaW4oc3R1ZHkucGFuZWwpICU+JQogIGdyb3VwX2J5KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sICBPcmlnaW4uVHJhY3QsIHN0YXJ0X2xvbiwgc3RhcnRfbGF0KSAlPiUKICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IHN1bShUcmlwX0NvdW50ZXIsIG5hLnJtPVQpKSAlPiUKICBsZWZ0X2pvaW4od2VhdGhlci5QYW5lbCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihpcy5uYShzdGFydF9zdGF0aW9uKSA9PSBGQUxTRSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApLAogICAgICAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwLCBsYWJlbCA9IFRSVUUpKSAlPiUKICBmaWx0ZXIoaXMubmEoT3JpZ2luLlRyYWN0KSA9PSBGQUxTRSkKCnJpZGUucGFuZWwgPC0KICBsZWZ0X2pvaW4ocmlkZS5wYW5lbCwgcGhpbENlbnN1cyAlPiUKICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KC1nZW9tZXRyeSksIGJ5ID0gYygiT3JpZ2luLlRyYWN0IiA9ICJHRU9JRCIpKQoKcmlkZS5wYW5lbCA8LQogIHJpZGUucGFuZWwgJT4lCiAgYXJyYW5nZShzdGFydF9zdGF0aW9uLCBpbnRlcnZhbDYwKSAlPiUKICBtdXRhdGUobGFnSG91ciA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwxKSwKICAgICAgICAgbGFnMkhvdXJzID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDIpLAogICAgICAgICBsYWczSG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMyksCiAgICAgICAgIGxhZzRIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCw0KSwKICAgICAgICAgbGFnMTJIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwxMiksCiAgICAgICAgIGxhZzFkYXkgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMjQpLAogICAgICAgICBob2xpZGF5ID0gaWZlbHNlKHlkYXkoaW50ZXJ2YWw2MCkgPT0gMTQ4LDEsMCkpICU+JQogICBtdXRhdGUoZGF5ID0geWRheShpbnRlcnZhbDYwKSkgJT4lCiAgIG11dGF0ZShob2xpZGF5TGFnID0gY2FzZV93aGVuKGRwbHlyOjpsYWcoaG9saWRheSwgMSkgPT0gMSB+ICJQbHVzT25lRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmxhZyhob2xpZGF5LCAyKSA9PSAxIH4gIlBsdXN0VHdvRGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsYWcoaG9saWRheSwgMykgPT0gMSB+ICJQbHVzdFRocmVlRGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsZWFkKGhvbGlkYXksIDEpID09IDEgfiAiTWludXNPbmVEYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAyKSA9PSAxIH4gIk1pbnVzVHdvRGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsZWFkKGhvbGlkYXksIDMpID09IDEgfiAiTWludXNUaHJlZURheXMiKSwKICAgICAgICAgaG9saWRheUxhZyA9IGlmZWxzZShpcy5uYShob2xpZGF5TGFnKSA9PSBUUlVFLCAwLCBob2xpZGF5TGFnKSkKYGBgCgojIyMgV2VhdGhlciBDb3JyZWxhdGlvbgoKV2l0aCB0aGUgc3R1ZHkgcGFuZWwsIHdlIGNvdWxkIHBlcmZvcm0gc29tZSBleHBsb3JhdG9yeSBhbmFseXNpcyB0byBwcmVwYXJlIGZvciBmdXJ0aGVyIHJlZ3Jlc3Npb24gbW9kZWwuIEZpcnN0LCB3ZSB3YW50IHRvIHNlZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRyaXAgY291bnRzIGFuZCB0aGUgd2VhdGhlciBjb25kaXRpb25zLiBUaGUgZmlndXJlIGJlbG93IHNob3dzIGEgZ2VuZXJhbGx5IHNpZ25pZmljYW50LCBwb3NpdGl2ZSwgYW5kIHN0cm9uZyBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGVtcGVyYXR1cmUgYW5kIHRyaXAgY291bnRzLiBUaGlzIG1lYW5zIGluIGdlbmVyYWwsIHdoZW4gdGhlIHRlbXBlcmF0dXJlIGluY3JlYXNlLCBiaWtlc2hhcmUgZGVtYW5kIHRlbmQgdG8gaW5jcmVhc2UgYXMgd2VsbC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KcmlkZS5wYW5lbCAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUgCiAgc3VtbWFyaXplKFRyaXBfQ291bnQgPSBtZWFuKFRyaXBfQ291bnQpLAogICAgICAgICAgICBUZW1wZXJhdHVyZSA9IGZpcnN0KFRlbXBlcmF0dXJlKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKFRlbXBlcmF0dXJlLCBUcmlwX0NvdW50KSkgKyAKICAgIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPSBGQUxTRSwgY29sb3I9IiMxMjRFNzgiKSArCiAgICBmYWNldF93cmFwKH53ZWVrLCBuY29sPTgpICsgCiAgICBsYWJzKHRpdGxlPSJUcmlwIENvdW50IEFzIGEgRnVjdGlvbiBvZiBUZW1wZXJhdHVyZSBieSBXZWVrIiwKICAgICAgICAgeD0iVGVtcGVyYXR1cmUiLCB5PSJNZWFuIFRyaXAgQ291bnQiKSArIAogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKCldlIHdvdWxkIGFsc28gbGlrZSB0byBzZWUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0cmlwIGNvdW50cyBhbmQgdGhlIHdpbmQgc3BlZWQuIFRoZSBmaWd1cmUgYmVsb3cgc2hvd3MgaW4gdGhlIGZpcnN0IHR3byB3ZWVrcywgdGhlcmUgaXMgYSBnZW5lcmFsbHkgbmVnYXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHdpbmQgc3BlZWQgYW5kIHRyaXAgY291bnRzLiBIb3dldmVyLCBpbiB0aGUgbGFzdCB0d28gd2Vla3MsIHRoZSByZWxhdGlvbnNoaXAgaXMgcG9zaXRpdmUuIFRoZXNlIG5vbi1jb25zdGFudCByZWxhdGlvbnNoaXAgbWF5IGluZGljYXRlIHRoYXQgdGhlIHdpbmQgc3BlZWQgaXMgbm90IHRoZSBzb2xlIHByZWRpY3RvcnMgaW5mbHVlbmNlIHRoZSByaWRlcnNoaXAuIAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQpyaWRlLnBhbmVsICU+JQogIGdyb3VwX2J5KGludGVydmFsNjApICU+JSAKICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IG1lYW4oVHJpcF9Db3VudCksCiAgICAgICAgICAgIFdpbmQgPSBmaXJzdChXaW5kX1NwZWVkKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKFdpbmQsIFRyaXBfQ291bnQpKSArIAogICAgZ2VvbV9wb2ludChjb2xvciA9ICJibGFjayIpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9IEZBTFNFLCBjb2xvcj0iIzEyNEU3OCIpICsKICAgIGZhY2V0X3dyYXAofndlZWssIG5jb2w9OCkgKyAKICAgIGxhYnModGl0bGU9IlRyaXAgQ291bnQgQXMgYSBGdWN0aW9uIG9mIFdpbmQgYnkgV2VlayIsCiAgICAgICAgIHg9IldpbmQgU3BlZWQiLCB5PSJNZWFuIFRyaXAgQ291bnQiKSArIAogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKSArIAogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyMjIFNlcmlhbCBDb3JyZWxhdGlvbgoKSWYgdHJpcCBjb3VudHMgZXhoaWJpdCB0ZW1wb3JhbCBhdXRvY29ycmVsYXRpb24sIGluY29ycG9yYXRpbmcgbGFnZ2VkIGZlYXR1cmVzIHdvdWxkIGltcHJvdmUgcHJlZGljdGlvbiBhY2N1cmFjeS4gVHJpcCB2b2x1bWVzIGF0IGEgZ2l2ZW4gaG91ciBhcmUgb2Z0ZW4gc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIHRob3NlIGluIHRoZSBwcmVjZWRpbmcgYW5kIGZvbGxvd2luZyBob3Vycywgc28ga25vd2luZyB0aGUgY291bnQgYXQgb25lIHRpbWUgcG9pbnQgbWFrZXMgaXQgZWFzaWVyIHRvIGVzdGltYXRlIHZvbHVtZXMgaW4gYWRqYWNlbnQgaW50ZXJ2YWxzLiBUbyBjYXB0dXJlIHRoaXMgZWZmZWN0LCB3ZSBlbmdpbmVlcmVkIHNpeCBkaXN0aW5jdCB0eXBlcyBvZiBob3VybHkgbGFnIGZlYXR1cmVzLgoKVGhlIHRhYmxlIGFuZCBjaGFydHMgYmVsb3cgc2hvdyB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbnMgYmV0d2VlbiBjdXJyZW50IHRyaXAgY291bnRzIGFuZCBzaXggbGFnZ2VkIHZhcmlhYmxlcy4gQ29ycmVsYXRpb24gc3RyZW5ndGggZmFsbHMgb2ZmIGFzIGxhZyBpbmNyZWFzZXM6IGltbWVkaWF0ZSAoMSBoKSBhbmQgZGFpbHkgKDI0IGgpIGxhZ3Mgc2hvdyB0aGUgc3Ryb25nZXN0IHBvc2l0aXZlIGxpbmtzLCBtaWQtcmFuZ2UgbGFncyB3ZWFrZW4gc3RlYWRpbHksIGFuZCB0aGUgMTItaG91ciBsYWcgZXZlbiBmbGlwcyB0byBhIG5lZ2F0aXZlIHJlbGF0aW9uc2hpcC4gVGhlc2UgcmVzdWx0cyBzaG93IHRoYXQgdGhlIGJlc3QgcHJlZGljdG9ycyBhcmUgdGhlIGhvdXIganVzdCBiZWZvcmUgYW5kIHRoZSBzYW1lIGhvdXIgb24gdGhlIHByZXZpb3VzIGRheSwgd2hpbGUgaG91cnMgZmFydGhlciBhd2F5IGFyZSBsZXNzIGhlbHBmdWzigJRhbmQgdGhlIGhvdXIgMTIgaG91cnMgZWFybGllciBjYW4gZXZlbiBzaG93IGFuIG9wcG9zaXRlIHRyZW5kLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgY29tcHV0ZSBtZWFuLWJ5LWhvdXIsIGdhdGhlciBsYWdzLCB0aGVuIGNvcnIgYnkgbGFnIHZhcmlhYmxlCmNvcnJfZGYgPC0gYXMuZGF0YS5mcmFtZShyaWRlLnBhbmVsKSAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgibGFnIiksICJUcmlwX0NvdW50IiksIG1lYW4sIG5hLnJtID0gVFJVRSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWludGVydmFsNjAsIC1UcmlwX0NvdW50KSAlPiUKICBtdXRhdGUoVmFyaWFibGUgPSBmYWN0b3IoCiAgICBWYXJpYWJsZSwKICAgIGxldmVscyA9IGMoImxhZ0hvdXIiLCJsYWcySG91cnMiLCJsYWczSG91cnMiLCJsYWc0SG91cnMiLCJsYWcxMkhvdXJzIiwibGFnMWRheSIpCiAgKSkgJT4lCiAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JQogIHN1bW1hcmlzZShDb3JyZWxhdGlvbiA9IHJvdW5kKGNvcihWYWx1ZSwgVHJpcF9Db3VudCksIDIpKSAlPiUKICB1bmdyb3VwKCkKCiMgcmVuZGVyIHdpdGgga2FibGVFeHRyYQpjb3JyX2RmICU+JQogIGthYmxlKAogICAgZm9ybWF0ID0gImh0bWwiLAogICAgY29sLm5hbWVzID0gYygiTGFnIFZhcmlhYmxlIiwgIkNvcnJlbGF0aW9uIHdpdGggVHJpcCBDb3VudCIpLAogICAgY2FwdGlvbiA9ICJDb3JyZWxhdGlvbiBCZXR3ZWVuIExhZ2dlZCBUcmlwIENvdW50cyBhbmQgQ3VycmVudCBUcmlwIENvdW50IgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIsCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikKICApCgpgYGAKCmBgYHtyLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQphcy5kYXRhLmZyYW1lKHJpZGUucGFuZWwpICU+JQogICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lIAogICAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImxhZyIpLCAiVHJpcF9Db3VudCIpLCBtZWFuLCBuYS5ybSA9IFRSVUUpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWludGVydmFsNjAsIC1UcmlwX0NvdW50KSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBWYWx1ZSAsIHkgPSBUcmlwX0NvdW50KSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAiYmxhY2siKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPSBGQUxTRSwgY29sb3I9IiMxMjRFNzgiKSArCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUsIG5jb2wgPSA2KSsKICBsYWJzKHggPSAiVmFyaWFibGUiLCB5ID0gIkNvcnJlbGF0aW9uIiwgdGl0bGUgPSAiQ29ycmVsYXRpb24gQmV0d2VlbiBTZXJpYWwgTGFnIFRyaXBzIGFuZCBUcmlwIENvdW50IikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKCgpgYGB7cn0KcmlkZS5UcmFpbiA8LSBmaWx0ZXIocmlkZS5wYW5lbCwgd2VlayA+PSAyMCkKcmlkZS5UZXN0IDwtIGZpbHRlcihyaWRlLnBhbmVsLCB3ZWVrIDwgMjApCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlZzEgPC0KICBsbShUcmlwX0NvdW50IH4gIGZhY3Rvcihob3VyKGludGVydmFsNjApKSArIGZhY3Rvcihkb3R3KSArIFRlbXBlcmF0dXJlLCAgZGF0YT1yaWRlLlRyYWluKQoKcmVnMiA8LQogIGxtKFRyaXBfQ291bnQgfiAgc3RhcnRfc3RhdGlvbiArICBmYWN0b3IoZG90dykrIFRlbXBlcmF0dXJlLCAgZGF0YT1yaWRlLlRyYWluKQoKcmVnMyA8LQogIGxtKFRyaXBfQ291bnQgfiAgc3RhcnRfc3RhdGlvbiArIGZhY3Rvcihob3VyKGludGVydmFsNjApKSArIGZhY3Rvcihkb3R3KSArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiwKICAgICBkYXRhPXJpZGUuVHJhaW4pCgpyZWc0IDwtCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgIGZhY3Rvcihob3VyKGludGVydmFsNjApKSArICBmYWN0b3IoZG90dykgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKwogICAgICAgICAgICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArbGFnM0hvdXJzICsgbGFnMTJIb3VycyArIGxhZzFkYXksCiAgICAgZGF0YT1yaWRlLlRyYWluKQoKcmVnNSA8LQogIGxtKFRyaXBfQ291bnQgfiAgc3RhcnRfc3RhdGlvbiArIGZhY3Rvcihob3VyKGludGVydmFsNjApKSArICBmYWN0b3IoZG90dykgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKwogICAgICAgICAgICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArbGFnM0hvdXJzICtsYWcxMkhvdXJzICsgbGFnMWRheSArIGhvbGlkYXlMYWcgKyBob2xpZGF5LAogICAgIGRhdGE9cmlkZS5UcmFpbikKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmlkZS5UZXN0LndlZWtOZXN0IDwtCiAgcmlkZS5UZXN0ICU+JQogIG5lc3QoLXdlZWspCmBgYAoKYGBge3J9Cm1vZGVsX3ByZWQgPC0gZnVuY3Rpb24oZGF0LCBmaXQpewogICBwcmVkIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gZGF0KX0KYGBgCgpgYGB7cn0Kd2Vla19wcmVkaWN0aW9ucyA8LQogIHJpZGUuVGVzdC53ZWVrTmVzdCAlPiUKICAgIG11dGF0ZShBVGltZV9GRSA9IG1hcCgueCA9IGRhdGEsIGZpdCA9IHJlZzEsIC5mID0gbW9kZWxfcHJlZCksCiAgICAgICAgICAgQlNwYWNlX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMiwgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBDVGltZV9TcGFjZV9GRSA9IG1hcCgueCA9IGRhdGEsIGZpdCA9IHJlZzMsIC5mID0gbW9kZWxfcHJlZCksCiAgICAgICAgICAgRFRpbWVfU3BhY2VfRkVfdGltZUxhZ3MgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWc0LCAuZiA9IG1vZGVsX3ByZWQpLAogICAgICAgICAgIEVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnNSwgLmYgPSBtb2RlbF9wcmVkKSkgJT4lCiAgICBnYXRoZXIoUmVncmVzc2lvbiwgUHJlZGljdGlvbiwgLWRhdGEsIC13ZWVrKSAlPiUKICAgIG11dGF0ZShPYnNlcnZlZCA9IG1hcChkYXRhLCBwdWxsLCBUcmlwX0NvdW50KSwKICAgICAgICAgICBBYnNvbHV0ZV9FcnJvciA9IG1hcDIoT2JzZXJ2ZWQsIFByZWRpY3Rpb24sICB+IGFicygueCAtIC55KSksCiAgICAgICAgICAgTUFFID0gbWFwX2RibChBYnNvbHV0ZV9FcnJvciwgbWVhbiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICBzZF9BRSA9IG1hcF9kYmwoQWJzb2x1dGVfRXJyb3IsIHNkLCBuYS5ybSA9IFRSVUUpKQpgYGAKYGBge3J9CndlZWtfcHJlZGljdGlvbnMgJT4lCiAgc2VsZWN0KHdlZWssIFJlZ3Jlc3Npb24sIE1BRSwgc2RfQUUpICU+JQogIGFycmFuZ2Uod2VlaywgUmVncmVzc2lvbikgJT4lCiAga2FibGUoCiAgICBmb3JtYXQgPSAiaHRtbCIsCiAgICBkaWdpdHMgPSAyLAogICAgY29sLm5hbWVzID0gYygiV2VlayIsICJNb2RlbCIsICJNZWFuIEFic29sdXRlIEVycm9yIiwgIlNEIG9mIEFic29sdXRlIEVycm9yIiksCiAgICBjYXB0aW9uID0gIldlZWtseSBNQUUgYW5kIFN0YW5kYXJkIERldmlhdGlvbiBvZiBBYnNvbHV0ZSBFcnJvcnMgYnkgUmVncmVzc2lvbiBNb2RlbCIKICApICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiLAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpCiAgKSAlPiUKICBjb2xsYXBzZV9yb3dzKAogICAgY29sdW1ucyA9IDEsCiAgICB2YWxpZ24gPSAidG9wIiwKICAgIHJvd19ncm91cF9sYWJlbF9wb3NpdGlvbiA9ICJzdGFjayIKICApCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQp3ZWVrX3ByZWRpY3Rpb25zICU+JQogIGRwbHlyOjpzZWxlY3Qod2VlaywgUmVncmVzc2lvbiwgTUFFKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIE1BRSwgLVJlZ3Jlc3Npb24sIC13ZWVrKSAlPiUKICBnZ3Bsb3QoYWVzKHdlZWssIE1BRSkpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsID0gUmVncmVzc2lvbiksIHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSkgKwogICAgbGFicyh0aXRsZSA9ICJNZWFuIEFic29sdXRlIEVycm9ycyBieSBtb2RlbCBzcGVjaWZpY2F0aW9uIGFuZCB3ZWVrIikgKwogIHBsb3RUaGVtZQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQp3ZWVrX3ByZWRpY3Rpb25zICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLVJlZ3Jlc3Npb24sIC1pbnRlcnZhbDYwLCAtc3RhcnRfc3RhdGlvbikgJT4lCiAgICBncm91cF9ieShSZWdyZXNzaW9uLCBWYXJpYWJsZSwgaW50ZXJ2YWw2MCkgJT4lCiAgICBzdW1tYXJpemUoVmFsdWUgPSBzdW0oVmFsdWUpKSAlPiUKICAgIGdncGxvdChhZXMoaW50ZXJ2YWw2MCwgVmFsdWUsIGNvbG91cj1WYXJpYWJsZSkpICsKICAgICAgZ2VvbV9saW5lKHNpemUgPSAxLjEpICsKICAgICAgZmFjZXRfd3JhcCh+UmVncmVzc2lvbiwgbmNvbD0xKSArCiAgICAgIGxhYnModGl0bGUgPSAiUHJlZGljdGVkL09ic2VydmVkIGJpa2Ugc2hhcmUgdGltZSBzZXJpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWE7IEEgdGVzdCBzZXQgb2YgMiB3ZWVrcyIsICB4ID0gIkhvdXIiLCB5PSAiU3RhdGlvbiBUcmlwcyIpICsKICAgICAgICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CndlZWtfcHJlZGljdGlvbnMgJT4lCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBzdGFydF9zdGF0aW9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X3N0YXRpb24pLAogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLAogICAgICAgICAgIHN0YXJ0X2xvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sb24pKSAlPiUKICAgIHNlbGVjdChpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCBzdGFydF9sb24sIHN0YXJ0X2xhdCwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24pICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIikgJT4lCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKZ2dwbG90KC4pKwogIGdlb21fc2YoZGF0YSA9IHBoaWxDZW5zdXMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHN0YXJ0X2xvbiwgeSA9IHN0YXJ0X2xhdCwgY29sb3IgPSBNQUUpLAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC43KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBsYWJzKHRpdGxlPSJNZWFuIEFicyBFcnJvciwgVGVzdCBTZXQsIE1vZGVsIDUiKSsKICBtYXBUaGVtZQpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CndlZWtfcHJlZGljdGlvbnMgJT4lCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBzdGFydF9zdGF0aW9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X3N0YXRpb24pLAogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLAogICAgICAgICAgIHN0YXJ0X2xvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sb24pLAogICAgICAgICAgIGRvdHcgPSBtYXAoZGF0YSwgcHVsbCwgZG90dykpICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwKICAgICAgICAgICBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSU+JQogIGdncGxvdCgpKwogIGdlb21fcG9pbnQoYWVzKHg9IE9ic2VydmVkLCB5ID0gUHJlZGljdGlvbikpKwogICAgZ2VvbV9zbW9vdGgoYWVzKHg9IE9ic2VydmVkLCB5PSBQcmVkaWN0aW9uKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikrCiAgICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDApKwogIGZhY2V0X2dyaWQodGltZV9vZl9kYXl+d2Vla2VuZCkrCiAgbGFicyh0aXRsZT0iT2JzZXJ2ZWQgdnMgUHJlZGljdGVkIiwKICAgICAgIHg9Ik9ic2VydmVkIHRyaXBzIiwKICAgICAgIHk9IlByZWRpY3RlZCB0cmlwcyIpKwogIHBsb3RUaGVtZQpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIHN0YXJ0X3N0YXRpb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbiksCiAgICAgICAgICAgc3RhcnRfbGF0ID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xhdCksCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbiksCiAgICAgICAgICAgZG90dyA9IG1hcChkYXRhLCBwdWxsLCBkb3R3KSApICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwKICAgICAgICAgICBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSAlPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uLCB3ZWVrZW5kLCB0aW1lX29mX2RheSwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKICBnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gcGhpbENlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gc3RhcnRfbG9uLCB5ID0gc3RhcnRfbGF0LCBjb2xvciA9IE1BRSksCiAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50Iiwgc2l6ZSA9IDMsIGFscGhhID0gMC40KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBmYWNldF9ncmlkKHdlZWtlbmR+dGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzb2x1dGUgRXJyb3JzLCBUZXN0IFNldCIpKwogIG1hcFRoZW1lCgpgYGAKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0gNH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIHN0YXJ0X3N0YXRpb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbiksCiAgICAgICAgICAgc3RhcnRfbGF0ID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xhdCksCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbiksCiAgICAgICAgICAgZG90dyA9IG1hcChkYXRhLCBwdWxsLCBkb3R3KSwKICAgICAgICAgICBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMgPSBtYXAoZGF0YSwgcHVsbCwgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zKSwKICAgICAgICAgICBNZWRfSW5jID0gbWFwKGRhdGEsIHB1bGwsIE1lZF9JbmMpLAogICAgICAgICAgIFBlcmNlbnRfV2hpdGUgPSBtYXAoZGF0YSwgcHVsbCwgUGVyY2VudF9XaGl0ZSkpICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwKICAgICAgICAgICBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcsIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgTWVkX0luYywgUGVyY2VudF9XaGl0ZSkgJT4lCiAgICB1bm5lc3QoKSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiRVRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3MiKSU+JQogIG11dGF0ZSh3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSwKICAgICAgICAgdGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSA3ICYgaG91cihpbnRlcnZhbDYwKSA8IDEwIH4gIkFNIFJ1c2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpICU+JQogIGZpbHRlcih0aW1lX29mX2RheSA9PSAiQU0gUnVzaCIpICU+JQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgTWVkX0luYywgUGVyY2VudF9XaGl0ZSkgJT4lCiAgc3VtbWFyaXplKE1BRSA9IG1lYW4oYWJzKE9ic2VydmVkLVByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKSU+JQogIGdhdGhlcigtc3RhcnRfc3RhdGlvbiwgLU1BRSwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSU+JQogIGdncGxvdCguKSsKICAjZ2VvbV9zZihkYXRhID0gY2hpY2Fnb0NlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gdmFsdWUsIHkgPSBNQUUpLCBhbHBoYSA9IDAuNCkrCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSB2YWx1ZSwgeSA9IE1BRSksIG1ldGhvZCA9ICJsbSIsIHNlPSBGQUxTRSkrCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikrCiAgbGFicyh0aXRsZT0iRXJyb3JzIGFzIGEgZnVuY3Rpb24gb2Ygc29jaW8tZWNvbm9taWMgdmFyaWFibGVzIiwKICAgICAgIHk9Ik1lYW4gQWJzb2x1dGUgRXJyb3IgKFRyaXBzKSIpKwogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKYGBgCg==